﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;

namespace JobShop
{
    [Description("An item representing a full set of objects in the system")]
    public class TcIndividual : IComparable<TcIndividual>, TiDuplicable<TcIndividual>
    {
        private readonly Int32 M;
        private readonly Int32 N;
        private readonly TcProblem Problem;
        private Int32[,] MachineOrder;
        public TcChromosome Chromosome { get; private set; }
        public TcPhenotype Phenotype { get; private set; }
        public TcSolution Solution { get; private set; }
        public Int32 Fitness { get; private set; }
        public Boolean Operated { get; private set; }
        //------------------------------------------------------------------------------

        public TcIndividual(TcProblem prmProblem)
        {
            Problem = prmProblem;
            M = prmProblem.M;
            N = prmProblem.N;
            Chromosome = new TcChromosome(prmProblem);
            Solution = new TcSolution(prmProblem.M, prmProblem.N);
            MachineOrder = new Int32[M, N];
            Operated = false;

            // Produce the phenotype from the genotype.
            ProducePhenotype();
        }
        //------------------------------------------------------------------------------

        [Description("This function that converts the binary chromosome into it's numeric representation (local harmonization)")]
        private void ProducePhenotype()
        {
            Phenotype = new TcPhenotype(M, N);
            Int32[,] priorMatrix = new Int32[N, N + 1];
            for (Int32 m = 0; m < M; m++)
            {
                Int32 count = 0;
                for (Int32 x = 0; x < N; x++)
                {
                    count = Convert.ToInt32(Math.Floor((1.0 * count) / M) * M) + Problem.TaskOrder[x, m];
                    priorMatrix[x, x] = -1;
                    for (Int32 y = x + 1; y < N; y++)
                    {
                        priorMatrix[x, y] = Chromosome.Genotype[count] ? 1 : 0;
                        priorMatrix[y, x] = (priorMatrix[x, y] + 1) % 2;
                        count += M;
                    }
                    Int32 max = 0;
                    for (Int32 y = 0; y < N; y++)
                        max += priorMatrix[x, y];
                    priorMatrix[x, N] = max + 1;
                }
                for (Int32 x = 0; x < N; x++)
                {
                    Int32 max = -1;
                    Int32 maxPos = 0;
                    for (Int32 y = 0; y < N; y++)
                    {
                        if (max < priorMatrix[y, N])
                        {
                            max = priorMatrix[y, N];
                            maxPos = y;
                        }
                    }
                    for (Int32 y = 0; y < N; y++)
                    {
                        if ((priorMatrix[maxPos, y] == 0) && (priorMatrix[y, N] >= 0))
                        {
                            priorMatrix[maxPos, y] = 1;
                            priorMatrix[y, maxPos] = 0;
                            Chromosome.Modify(maxPos, y, m);
                        }
                    }
                    priorMatrix[maxPos, N] = -1;
                    Phenotype.Solution[m, x] = maxPos;
                    MachineOrder[m, maxPos] = x;
                }
            }
            Operated = true;
        }
        //------------------------------------------------------------------------------

        [Description("This function apply the reordering to make the phenotype feasible (global harmonization)")]
        private void MakeFeasible(Int32[] machineFront, Int32[] jobFront)
        {
            Int32[,] hPrior = new Int32[M, N * 2 + 1];
            for (Int32 j = 0; j < N; j++)
            {
                if (jobFront[j] < M)
                {
                    Int32 machine = Problem.Machines[j, jobFront[j]];
                    Int32 pos = FindTaskLocation(j, machine);
                    Int32 x;
                    for (x = 0; x < (2 * hPrior[machine, 2 * N]); x += 2)
                    {
                        if (hPrior[machine, x + 1] > pos)
                            break;
                    }
                    for (Int32 y = (2 * hPrior[machine, 2 * N] - 1); y >= x; y -= 2)
                    {
                        hPrior[machine, y + 2] = hPrior[machine, y];
                        hPrior[machine, y + 1] = hPrior[machine, y - 1];
                    }
                    hPrior[machine, x] = j;
                    hPrior[machine, x + 1] = pos;
                    hPrior[machine, 2 * N]++;
                }
            }

            for (Int32 m = 0; m < M; m++)
            {
                for (Int32 x = 0; x < hPrior[m, 2 * N]; x++)
                {
                    for (Int32 y = hPrior[m, 2 * x + 1]; y > (machineFront[m] + x); y--)
                    {
                        Int32 temp = Phenotype.Solution[m, y];
                        Phenotype.Solution[m, y] = Phenotype.Solution[m, y - 1];
                        Phenotype.Solution[m, y - 1] = temp;
                        MachineOrder[m, Phenotype.Solution[m, y - 1]]--;
                        MachineOrder[m, Phenotype.Solution[m, y]]++;
                    }
                }
            }
        }
        //------------------------------------------------------------------------------

        [Description("Find a task in the phenotype")]
        private Int32 FindTaskLocation(Int32 prmJob, Int32 prmMachine)
        {
            for (Int32 x = 0; x < N; x++)
            {
                if (Phenotype.Solution[prmMachine, x] == prmJob)
                    return x;
            }
            return -1;
        }
        //------------------------------------------------------------------------------

        [Description("Find a feasible gap and place the job inside. Otherwise place in default position.")]
        private Boolean AdjustGap(Int32 prmJob, Int32 prmMachine, Int32[] machineBusy, Int32[] jobBusy, Int32[] machineFront, Int32[] jobFront)
        {
            Int32 busy = 0;
            Int32 startTime = 0;
            Int32 pos;
            for (pos = 0; pos < machineFront[prmMachine]; pos++)
	        {
                Int32 cluster = ((jobBusy[prmJob] > busy) ? (jobBusy[prmJob] - busy) : 0);
                Int32 slot = (Solution.Solution[prmMachine, pos * 6 + 2] - Solution.Solution[prmMachine, pos * 6 + 1]) - (cluster + Problem.Times[prmJob, jobFront[prmJob]]);
                if (slot >= 0)
		        {
                    Solution.Solution[prmMachine, pos * 6 + 1] = Solution.Solution[prmMachine, pos * 6 + 2] - slot;
                    startTime = Solution.Solution[prmMachine, pos * 6 + 1] - Problem.Times[prmJob, jobFront[prmJob]];
                    for (Int32 z = machineFront[prmMachine]; z > pos; z--)
			        {
                        Phenotype.Solution[prmMachine, z] = Phenotype.Solution[prmMachine, z - 1];
                        MachineOrder[prmMachine, Phenotype.Solution[prmMachine, z - 1]] = z;
                        for (Int32 s = 0; s < 6; s++)
                            Solution.Solution[prmMachine, z * 6 + s] = Solution.Solution[prmMachine, (z - 1) * 6 + s];
			        }
			        goto label_makeschedule;
		        }
                busy += (Solution.Solution[prmMachine, pos * 6 + 5] - Solution.Solution[prmMachine, pos * 6 + 1]);
	        }
            startTime = Math.Max(jobBusy[prmJob], busy);

        label_makeschedule:
            Phenotype.Solution[prmMachine, pos] = prmJob;
            MachineOrder[prmMachine, prmJob] = pos;
            Solution.Solution[prmMachine, pos * 6] = -1;
            Solution.Solution[prmMachine, pos * 6 + 1] = ((pos > 0) ? (Solution.Solution[prmMachine, (pos - 1) * 6 + 5]) : 0);
            Solution.Solution[prmMachine, pos * 6 + 2] = startTime;
            Solution.Solution[prmMachine, pos * 6 + 3] = prmJob;
            Solution.Solution[prmMachine, pos * 6 + 4] = Solution.Solution[prmMachine, pos * 6 + 2];
            Solution.Solution[prmMachine, pos * 6 + 5] = Solution.Solution[prmMachine, pos * 6 + 4] + Problem.Times[prmJob, jobFront[prmJob]];
            machineBusy[prmMachine] = Solution.Solution[prmMachine, machineFront[prmMachine] * 6 + 5];
            jobBusy[prmJob] = Solution.Solution[prmMachine, pos * 6 + 5];
            machineFront[prmMachine]++;
	        jobFront[prmJob]++;
	        return true;
        }
        //------------------------------------------------------------------------------

        public Int32 Evaluate()
        {
            if (!Operated)
                return Fitness;

            Int32 count = 0;
            Int32 scheduleTime = 0;
            Boolean[,] operationFlag = new Boolean[N, M];
            Random rand = new Random();
            Int32[] machineBusy = new Int32[M];
            Int32[] machineFront = new Int32[M];
            Int32[] jobBusy = new Int32[N];
            Int32[] jobFront = new Int32[N];
            
            while(true)
            {
                Int32 currentMachine = rand.Next() % M;
                Int32 infTry = 0;
                for (Int32 x = 0; infTry < M; x++)
                {
                    Int32 machine = (currentMachine + x) % M;
                    if (machineFront[machine] >= N)
                    {
                        infTry++;
                        continue;
                    }
                    Int32 job = Phenotype.Solution[machine, machineFront[machine]];
                    if (Problem.Machines[job, jobFront[job]] == machine)
                    {
                        infTry = 0;
                        AdjustGap(job, machine, machineBusy, jobBusy, machineFront, jobFront);
                        operationFlag[job, Problem.TaskOrder[job, machine]] = true;
                        scheduleTime = Math.Max(scheduleTime, Math.Max(jobBusy[job], machineBusy[machine]));
                        count++;
                        if (count == M * N)
                        {
                            Chromosome.Build(Phenotype);
                            Fitness = scheduleTime;
                            Solution.Fitness = scheduleTime;
                            Operated = false;
                            return Fitness;
                        }
                    }
                    else infTry++;
                }
                MakeFeasible(machineFront, jobFront);
            }
        }
        //------------------------------------------------------------------------------

        public Boolean OnePointBinaryCrossover(Int32 prmPoint, TcIndividual prmIndividual)
        {
            if (prmPoint <= 0 || prmPoint >= Chromosome.Length)
                return false;

            Boolean[] bits = Chromosome.ReplaceBits(0, prmPoint, prmIndividual.Chromosome.GetBits(0, prmPoint));
            prmIndividual.Chromosome.ReplaceBits(0, prmPoint, bits);
            ProducePhenotype();
            prmIndividual.ProducePhenotype();
            return true;
        }
        //------------------------------------------------------------------------------

        public Boolean TwoPointBinaryCrossover(Int32 prmP1, Int32 prmP2, TcIndividual prmIndividual)
        {
            if (prmP1 <= 0 || prmP2 >= Chromosome.Length || prmP1 > prmP2)
                return false;

            Boolean[] bits = Chromosome.ReplaceBits(prmP1, prmP2, prmIndividual.Chromosome.GetBits(prmP1, prmP2));
            prmIndividual.Chromosome.ReplaceBits(prmP1, prmP2, bits);
            ProducePhenotype();
            prmIndividual.ProducePhenotype();
            return true;
        }
        //------------------------------------------------------------------------------

        public Boolean BitFlipMutation(Int32 prmCount)
        {
            Boolean result = true;
            Random rand = new Random();
            for (int i = 0; i < prmCount; i++)
                result = result && Chromosome.FlipBit(rand.Next(0, Chromosome.Length - 1));
            ProducePhenotype();
            return result;
        }
        //------------------------------------------------------------------------------

        // IComparable
        public int CompareTo(TcIndividual other)
        {
            if (Fitness < other.Fitness)
                return -1;
            else if (Fitness > other.Fitness)
                return 1;
            return 0;
        }
        //------------------------------------------------------------------------------

        // TiDuplicable
        public TcIndividual Duplicate()
        {
            return Clone() as TcIndividual;
        }
        //------------------------------------------------------------------------------

        public object Clone()
        {
            TcIndividual clone = new TcIndividual(Problem);
            clone.Chromosome = Chromosome.Duplicate();
            clone.Phenotype = Phenotype.Duplicate();
            clone.Solution = Solution.Duplicate();
            clone.Fitness = Fitness;
            clone.Operated = Operated;
            return clone;
        }
        //------------------------------------------------------------------------------

    }
    //------------------------------------------------------------------------------

}
//------------------------------------------------------------------------------